﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable
using Windows.Win32.UI.ColorSystem;

namespace System.Windows.Forms.Metafiles;

internal readonly unsafe struct EmfRecord
{
    public HDC HDC { get; }
    private readonly HANDLETABLE* _lpht;
    private readonly ENHMETARECORD* _lpmr;
    private readonly int _nHandles;
    public LPARAM Data { get; }

    public EmfRecord(
        HDC hdc,
        HANDLETABLE* lpht,
        ENHMETARECORD* lpmr,
        int nHandles,
        LPARAM data)
    {
        HDC = hdc;
        _lpht = lpht;
        _lpmr = lpmr;
        _nHandles = nHandles;
        Data = data;
    }

    public ENHANCED_METAFILE_RECORD_TYPE Type => _lpmr->iType;
    public ReadOnlySpan<uint> Params => _lpmr->dParm.AsReadOnlySpan();
    public ReadOnlySpan<HGDIOBJ> Handles => new(_lpht, _nHandles);

    public ENHMETAHEADER* HeaderRecord => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_HEADER ? (ENHMETAHEADER*)_lpmr : null;
    public EMREXTSELECTCLIPRGN* ExtSelectClipRgnRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_EXTSELECTCLIPRGN ? (EMREXTSELECTCLIPRGN*)_lpmr : null;
    public EMRPOINTRECORD* SetViewportOrgExRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_SETVIEWPORTORGEX ? (EMRPOINTRECORD*)_lpmr : null;
    public EMRPOINTRECORD* SetBrushOrgExRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_SETBRUSHORGEX ? (EMRPOINTRECORD*)_lpmr : null;
    public EMRPOINTRECORD* SetWindowOrgExRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_SETWINDOWORGEX ? (EMRPOINTRECORD*)_lpmr : null;
    public EMRPOINTRECORD* OffsetClipRgnRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_OFFSETCLIPRGN ? (EMRPOINTRECORD*)_lpmr : null;
    public EMRPOINTRECORD* MoveToExRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_MOVETOEX ? (EMRPOINTRECORD*)_lpmr : null;
    public EMRPOINTRECORD* LineToRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_LINETO ? (EMRPOINTRECORD*)_lpmr : null;
    public EMRCREATEBRUSHINDIRECT* CreateBrushIndirectRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_CREATEBRUSHINDIRECT ? (EMRCREATEBRUSHINDIRECT*)_lpmr : null;
    public EMRENUMRECORD<R2_MODE>* SetROP2Record
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_SETROP2 ? (EMRENUMRECORD<R2_MODE>*)_lpmr : null;
    public EMRENUMRECORD<BACKGROUND_MODE>* SetBkModeRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_SETBKMODE ? (EMRENUMRECORD<BACKGROUND_MODE>*)_lpmr : null;
    public EMRCREATEPEN* CreatePenRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_CREATEPEN ? (EMRCREATEPEN*)_lpmr : null;
    public EMREXTCREATEPEN* ExtCreatePenRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_EXTCREATEPEN ? (EMREXTCREATEPEN*)_lpmr : null;
    public EMRINDEXRECORD* SelectObjectRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_SELECTOBJECT ? (EMRINDEXRECORD*)_lpmr : null;
    public EMRINDEXRECORD* DeleteObjectRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_DELETEOBJECT ? (EMRINDEXRECORD*)_lpmr : null;
    public EMRBITBLT* BitBltRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_BITBLT ? (EMRBITBLT*)_lpmr : null;
    public EMRENUMRECORD<ICM_MODE>* SetIcmModeRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_SETICMMODE ? (EMRENUMRECORD<ICM_MODE>*)_lpmr : null;
    public EMRPOLY16* Polygon16Record
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYGON16 ? (EMRPOLY16*)_lpmr : null;
    public EMRPOLY16* Polyline16Record
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYLINE16 ? (EMRPOLY16*)_lpmr : null;
    public EMRPOLY16* PolyBezier16Record
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYBEZIER16 ? (EMRPOLY16*)_lpmr : null;
    public EMRPOLY16* PolylineTo16Record
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYLINETO16 ? (EMRPOLY16*)_lpmr : null;
    public EMRPOLY16* PolyBezierTo16Record
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYBEZIERTO16 ? (EMRPOLY16*)_lpmr : null;
    public EMRPOLYPOLY16* PolyPolyline16Record
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYPOLYLINE16 ? (EMRPOLYPOLY16*)_lpmr : null;
    public EMRPOLYPOLY16* PolyPolygon16Record
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYPOLYGON16 ? (EMRPOLYPOLY16*)_lpmr : null;
    public EMRSETWORLDTRANSFORM* SetWorldTransformRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_SETWORLDTRANSFORM ? (EMRSETWORLDTRANSFORM*)_lpmr : null;
    public EMRMODIFYWORLDTRANSFORM* ModifyWorldTransformRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_MODIFYWORLDTRANSFORM ? (EMRMODIFYWORLDTRANSFORM*)_lpmr : null;
    public EMRSETCOLOR* SetBkColorRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_SETBKCOLOR ? (EMRSETCOLOR*)_lpmr : null;
    public EMRSETCOLOR* SetTextColorRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_SETTEXTCOLOR ? (EMRSETCOLOR*)_lpmr : null;
    public EMRCREATEDIBPATTERNBRUSHPT* CreateDibPatternBrushPtRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_CREATEDIBPATTERNBRUSHPT ? (EMRCREATEDIBPATTERNBRUSHPT*)_lpmr : null;
    public EMRENUMRECORD<TEXT_ALIGN_OPTIONS>* SetTextAlignRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_SETTEXTALIGN ? (EMRENUMRECORD<TEXT_ALIGN_OPTIONS>*)_lpmr : null;
    public EMREXTCREATEFONTINDIRECTW* ExtCreateFontIndirectWRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_EXTCREATEFONTINDIRECTW ? (EMREXTCREATEFONTINDIRECTW*)_lpmr : null;
    public EMREXTTEXTOUTW* ExtTextOutWRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_EXTTEXTOUTW ? (EMREXTTEXTOUTW*)_lpmr : null;
    public EMRENUMRECORD<HDC_MAP_MODE>* SetMapModeRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_SETMAPMODE ? (EMRENUMRECORD<HDC_MAP_MODE>*)_lpmr : null;
    public EMRRECTRECORD* FillPathRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_FILLPATH ? (EMRRECTRECORD*)_lpmr : null;
    public EMRRECTRECORD* StrokeAndFillPathRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_STROKEANDFILLPATH ? (EMRRECTRECORD*)_lpmr : null;
    public EMRRECTRECORD* StrokePathRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_STROKEPATH ? (EMRRECTRECORD*)_lpmr : null;
    public EMRRECTRECORD* ExcludeClipRectRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_EXCLUDECLIPRECT ? (EMRRECTRECORD*)_lpmr : null;
    public EMRRECTRECORD* IntersetClipRectRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_INTERSECTCLIPRECT ? (EMRRECTRECORD*)_lpmr : null;
    public EMRRECTRECORD* EllipseRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_ELLIPSE ? (EMRRECTRECORD*)_lpmr : null;
    public EMRRECTRECORD* RectangleRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_RECTANGLE ? (EMRRECTRECORD*)_lpmr : null;
    public EMRRESTOREDC* RestoreDCRecord
        => Type == ENHANCED_METAFILE_RECORD_TYPE.EMR_RESTOREDC ? (EMRRESTOREDC*)_lpmr : null;

    public override string ToString() => Type switch
    {
        // Note that not all records have special handling yet- we're filling these in as we go.
        ENHANCED_METAFILE_RECORD_TYPE.EMR_HEADER => HeaderRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_EXTSELECTCLIPRGN => ExtSelectClipRgnRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_SETVIEWPORTORGEX => SetViewportOrgExRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_SETWINDOWORGEX => SetWindowOrgExRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_OFFSETCLIPRGN => OffsetClipRgnRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_MOVETOEX => MoveToExRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_LINETO => LineToRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_CREATEBRUSHINDIRECT => CreateBrushIndirectRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_SETBKMODE => SetBkModeRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_SETROP2 => SetROP2Record->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_CREATEPEN => CreatePenRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_EXTCREATEPEN => ExtCreatePenRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_SELECTOBJECT => SelectObjectRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_DELETEOBJECT => DeleteObjectRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_BITBLT => BitBltRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_SETICMMODE => SetIcmModeRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYLINE16 => Polyline16Record->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYBEZIER16 => PolyBezier16Record->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYGON16 => Polygon16Record->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYBEZIERTO16 => PolyBezierTo16Record->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYLINETO16 => PolylineTo16Record->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYPOLYGON16 => PolyPolygon16Record->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYPOLYLINE16 => PolyPolyline16Record->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_SETWORLDTRANSFORM => SetWorldTransformRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_MODIFYWORLDTRANSFORM => ModifyWorldTransformRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_SETTEXTCOLOR => SetTextColorRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_SETBKCOLOR => SetBkColorRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_CREATEDIBPATTERNBRUSHPT => CreateDibPatternBrushPtRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_SETTEXTALIGN => SetTextAlignRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_EXTCREATEFONTINDIRECTW => ExtCreateFontIndirectWRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_EXTTEXTOUTW => ExtTextOutWRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_SETMAPMODE => SetMapModeRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_FILLPATH => FillPathRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_STROKEANDFILLPATH => StrokeAndFillPathRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_STROKEPATH => StrokePathRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_EXCLUDECLIPRECT => ExcludeClipRectRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_INTERSECTCLIPRECT => IntersetClipRectRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_ELLIPSE => EllipseRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_RECTANGLE => RectangleRecord->ToString(),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_RESTOREDC => RestoreDCRecord->ToString(),
        _ => $"[EMR{Type}]"
    };

    public string ToString(DeviceContextState state) => Type switch
    {
        ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYLINE16 => Polyline16Record->ToString(state),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYGON16 => Polygon16Record->ToString(state),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYPOLYGON16 => PolyPolygon16Record->ToString(state),
        ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYPOLYLINE16 => PolyPolyline16Record->ToString(state),
        _ => ToString()
    };
}
